Quad4 Git (Main)
>Reticulum MeshChatX
A extensively modified and feature-rich fork of Reticulum MeshChat by Liam Cottle.
This project is independent from the original Reticulum MeshChat project and is not affiliated with it.
• Website: meshchatx.com
• Source: github.com/Quad4-Software/MeshChatX
• Mirror: lavaforge.org/Reticulum-Things/MeshChatX
• Releases: github.com/Quad4-Software/MeshChatX
• Changelog: CHANGELOG.md
• LXMF: T383838f489752fbef161c64d65e385a4e9fc74
• Umbrel App Store: apps.umbrel.com/app/meshchatx
<a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/Quad4-Software/MeshChatX"><img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" height="60" alt="Get it on Obtainium"></a>
rngit NomadNet Node: T383838132f67e79d9b24aad014e93015fb858f:/page/index.mu
rngit: T383838git clone rns://06a54b505bb67b25ef3f8097e8001edc/public/MeshChatX
Important Changes from Reticulum MeshChat
• Uses LXST for calls
• Integrates RRC
• Expanded tools
• Map w/ MBTiles support, remote KMZ/KML/GeoJSON overlays (NomadNet T383838/file/ and RNGit sparse fetch)
• Panes and Tabs
• Replaced Peewee ORM with raw SQL.
• Replaced Axios with native fetch.
• Uses Electron 41.x (bundled Node 24 runtime).
• .whls ships with webserver and built-in frontend assets for more deployment options.
• i18n
• PNPM and UV for dependency management.
Requirements
• Python T383838>=3.11 (from T383838pyproject.toml)
• Node.js T383838>=24 (from T383838package.json T383838engines)
• pnpm T38383811.1.2 (from T383838package.json T383838packageManager)
• UV (used by T383838Taskfile.yml and CI workflows)
Browser Versions Required:
Safari 16.4 or later, Chrome 111 or later, Firefox 128 or later (bundled web UI).
T282828
task install
task format
task lint
task test
task build
Install Methods
Use the method that matches your environment and packaging preference.
┌───────────────────────┬──────────────────────────┬─────────────────────────────────────────┬─────┐
│ Method │ Includes frontend assets │ Architectures │ Be… │
├───────────────────────┼──────────────────────────┼─────────────────────────────────────────┼─────┤
│ Docker image │ Yes │ T383838linux/amd64, T383838linux/arm64 │ Fa… │
│ Python wheel (T383838.whl) │ Yes │ Any Python-supported architecture │ He… │
│ Linux AppImage │ Yes │ T383838x64, T383838arm64 │ Po… │
│ Debian package (T383838.deb) │ Yes │ T383838x64, T383838arm64 │ De… │
│ RPM package (T383838.rpm) │ Yes │ CI-runner dependent for published arti… │ Fe… │
│ From source │ Built locally │ Host architecture │ De… │
└───────────────────────┴──────────────────────────┴─────────────────────────────────────────┴─────┘
Notes:
• GitHub Actions builds tagged releases (Linux wheel/AppImage/deb/rpm, Windows, macOS, Flatpak, Android APKs when the tag is on T383838dev or T383838master, SLSA, draft release) in one run via T383838.github/workflows/build-release.yml; the container image via T383838.github/workflows/docker.yml. Branch and PR Android CI stays in T383838.github/workflows/android-build.yml.
• Linux T383838x64 and T383838arm64 AppImage + DEB are built on GitHub; RPM is attempted and uploaded when produced.
Docker
• Docker Hub: T383838quad4io/meshchatx
• GHCR: T383838ghcr.io/quad4-software/meshchatx
• Variants: default tags (for example T383838:latest) are the standard Alpine image. Use T383838-hardened for the Chainguard/Wolfi image, or T383838-extra for Alpine plus i2pd and yggdrasil (for example T383838:latest-extra, built from the same T383838Dockerfile with T383838VARIANT=extra).
T282828
docker compose up -d
T282828
docker run -d --name reticulum-meshchatx \\
--restart unless-stopped \\
--init \\
--user 1000:1000 \\
--security-opt no-new-privileges:true \\
--cap-drop ALL \\
--read-only \\
--tmpfs /tmp:noexec,nosuid,size=256m \\
--tmpfs /home/meshchat:nosuid,size=64m \\
--cpus=2.0 \\
--memory=1g \\
--memory-reservation=256m \\
--pids-limit=512 \\
-p 127.0.0.1:8000:8000 \\
-v meshchatx-config:/config \\
ghcr.io/quad4-software/meshchatx:latest
You can substitute T383838quad4io/meshchatx:latest for the image if you prefer Docker Hub.
Default compose file maps:
• T383838127.0.0.1:8000 on host -> container port T3838388000
• Docker named volume T383838meshchatx-config -> T383838/config for persistence (works with the image T383838meshchat user, UID 1000, without bind-mount permission fixes)
Optional: bind mount a host directory instead
If you want data under a host path (for example T383838./meshchat-config), replace the volume line with T383838-v "$(pwd)/meshchat-config:/config" (Compose: change the service T383838volumes entry to that bind path). The container runs as UID 1000; the host directory must be writable by that uid (typical fix: T383838sudo chown -R 1000:1000 ./meshchat-config). If the directory is empty on first run, create it first so Docker does not create it as root-only.
Inspect or reset the named volume
T282828
docker volume inspect meshchatx-config
# remove container and delete persisted data (destructive)
docker rm -f reticulum-meshchatx
docker volume rm meshchatx-config
Install from Release Artifacts
1) Linux AppImage (x64/arm64)
1. Download T383838ReticulumMeshChatX-v<version>-linux-<arch>.AppImage from releases.
2. Make it executable and run:
T282828
chmod +x ./ReticulumMeshChatX-v*-linux-*.AppImage
./ReticulumMeshChatX-v*-linux-*.AppImage
2) Debian/Ubuntu T383838.deb (x64/arm64)
1. Download T383838ReticulumMeshChatX-v<version>-linux-<arch>.deb.
2. Install:
T282828
sudo apt install ./ReticulumMeshChatX-v*-linux-*.deb
3) RPM-based systems
1. Download T383838ReticulumMeshChatX-v<version>-linux-<arch>.rpm if present in the release.
2. Install with your distro tool:
T282828
sudo rpm -Uvh ./ReticulumMeshChatX-v*-linux-*.rpm
4) Python wheel (T383838.whl)
Release wheels include the built web assets.
T282828
pip install ./reticulum_meshchatx-*-py3-none-any.whl
meshchatx --headless
T383838pipx is also supported:
T282828
pipx install ./reticulum_meshchatx-*-py3-none-any.whl
Run from Source (Web Server Mode)
Use this when developing or when you need a local custom build.
T282828
git clone https://github.com/Quad4-Software/MeshChatX.git
cd MeshChatX
corepack enable
pnpm config set verify-store-integrity true
pnpm install --frozen-lockfile
pip install "uv==0.11.15"
uv lock --check
uv sync --group dev
pnpm run build-frontend
uv run python -m meshchatx.meshchat --headless --host 127.0.0.1
Notes on the install commands above:
• T383838pnpm install --frozen-lockfile refuses to update T383838pnpm-lock.yaml and fails if the lockfile does not match T383838package.json. This is what blocks an unexpected upstream version from being silently pulled in.
• T383838verify-store-integrity=true is also set in the project T383838pnpm-workspace.yaml; the explicit T383838pnpm config set line above just hardens the user-level config too.
• Lifecycle scripts (T383838preinstall/T383838postinstall) are blocked by default in pnpm v11+. Only the packages listed under T383838allowBuilds in T383838pnpm-workspace.yaml are allowed to run install scripts (currently T383838electron, T383838electron-winstaller, T383838esbuild).
• T383838uv lock --check fails fast if T383838uv.lock is out of sync with T383838pyproject.toml; T383838uv sync then resolves only from the lockfile.
• For a strict lockfile-only UV install (no implicit lockfile refresh), pin UV with T383838pip install "uv==0.11.15" to match what CI uses.
If you intentionally want to update dependencies, run T383838pnpm update / T383838uv lock in a dedicated commit and review the resulting lockfile diff before pushing.
Run sandboxed (Linux)
To run the native T383838meshchatx binary (alias: T383838meshchat) with extra filesystem isolation, you can use Firejail or Bubblewrap (T383838bwrap) while keeping normal network access for Reticulum and the web UI. Full examples (pip/pipx, Poetry, USB serial notes) are in:
The same page appears in the in-app Documentation list (MeshChatX docs) when served from the bundled or synced T383838meshchatx-docs files.
Linux desktop: emoji fonts
The emoji picker renders standard Unicode emoji using your system fonts (Electron/Chromium). If emoji show as empty squares (“tofu”), install a color emoji package and restart the app.
┌────────────────────────────┬──────────────────────────────────────────────────────────────────┐
│ Family (examples) │ Package │
├────────────────────────────┼──────────────────────────────────────────────────────────────────┤
│ Arch Linux, Artix, Manjaro │ T383838noto-fonts-emoji (T383838sudo pacman -S noto-fonts-emoji) │
│ Debian, Ubuntu │ T383838fonts-noto-color-emoji (T383838sudo apt install fonts-noto-color-emoji) │
│ Fedora │ T383838google-noto-emoji-color-fonts │
└────────────────────────────┴──────────────────────────────────────────────────────────────────┘
After installing, run T383838fc-cache -fv if glyphs still fail until the next login. Optional: T383838noto-fonts for broader symbol coverage on minimal installs.
Windows desktop: microphone (Electron, Windows 10 / 11)
Calls and voice attachments use the microphone through Chromium inside the desktop app. If the UI shows no access or getUserMedia fails, check Windows privacy first (this is a frequent cause for “classic” Win32 apps, including Electron):
1. Press Win + R, paste T383838ms-settings:privacy-microphone, press Enter.
2. Turn Microphone access on.
3. Enable Let desktop apps access your microphone (wording may vary slightly by Windows version).
4. Ensure MeshChatX is not denied under Choose which apps can access your microphone if that list appears.
Also confirm the app is not muted in Settings → System → Sound and that a working input device is selected.
Offline Builds
MeshChatX supports two levels of offline building:
1. Cached offline builds: you already ran T383838make install once and have T383838node_modules/, T383838.venv/, and local caches.
2. Air-gapped (zero-network) builds: the machine has never had internet. You create an offline bundle on a networked machine and transfer it.
Cached Offline Builds
Set T383838MESHCHATX_OFFLINE_BUILD=1 before running any build command. This skips all network fetches (micron-parser-go WASM, Reticulum manual, repository wheels) and runs package managers in offline mode. If a required cached asset is missing, the build fails with a clear error instead of hanging.
T282828
# Install dependencies offline (requires populated pnpm store and uv cache)
MESHCHATX_OFFLINE_BUILD=1 make install
# Build frontend + backend offline
MESHCHATX_OFFLINE_BUILD=1 pnpm run build:offline
# Build Linux desktop packages offline
MESHCHATX_OFFLINE_BUILD=1 pnpm run dist:linux:offline
# Android Gradle also respects the flag
MESHCHATX_OFFLINE_BUILD=1 ./gradlew :app:assembleRelease
│ Note: Cached offline mode only skips build-time network access. The first T383838make install must be
│ run online (or with pre-populated caches) so that T383838pnpm and T383838uv have the packages available
│ locally.
Air-Gapped Builds (No Cache)
For machines with no internet access at all, create an offline bundle on a networked machine and transfer it.
On the online machine:
T282828
# Create the bundle (includes node_modules, Python wheels, and tooling caches)
pnpm run bundle:offline
# Optional: also pre-download packaging tools (appimagetool, etc.)
bash scripts/create-offline-bundle.sh --warm-packaging
# Transfer the bundle to your air-gapped machine
tar czf meshchatx-offline-linux-x64.tar.gz -C vendor/offline meshchatx-offline-bundle-*/
On the air-gapped machine:
T282828
# Extract the bundle into the project
tar xzf meshchatx-offline-linux-x64.tar.gz
# Install from the bundle (extracts node_modules and sets up caches)
bash scripts/install-offline.sh
# Build completely offline
MESHCHATX_OFFLINE_BUILD=1 make build
# Or package offline
MESHCHATX_OFFLINE_BUILD=1 pnpm run dist:linux
The bundle is platform-specific because it contains native binaries (Electron, esbuild, etc.). Create it on the same OS/architecture as the air-gapped build host.
Prerequisites on the air-gapped machine: T383838node, T383838pnpm, T383838uv, and T383838python3 must be installed (the bundle provides all dependencies and caches, not the toolchain itself).
│ Android builds: The offline bundle does not include Android Chaquopy wheels. Build those
│ separately on an online machine (T383838bash scripts/build-android-wheels-local.sh) and copy
│ T383838android/vendor/ to the air-gapped host alongside the project. Then run Gradle with
│ T383838MESHCHATX_OFFLINE_BUILD=1.
Build Desktop Packages from Source
These scripts are defined in T383838package.json and T383838Taskfile.yml.
Linux x64 AppImage + DEB
T282828
pnpm run dist:linux-x64
Linux arm64 AppImage + DEB
T282828
pnpm run dist:linux-arm64
RPM
T282828
pnpm run dist:rpm
Or through Task:
T282828
task dist:fe:rpm
Container build (wheel, AppImage, deb, rpm)
T383838Dockerfile.build runs the same shell-driven steps CI uses (Poetry, pnpm, T383838task, packaging APT deps). It is oriented toward linux/amd64 (NodeSource amd64 tarball, Task amd64 binary). Default target is everything; override with a build arg.
Targets for T383838MESHCHATX_BUILD_TARGETS: T383838all (default), T383838wheel, or T383838electron (AppImage + deb for x64 and arm64, best-effort RPM, no wheel).
Build:
T282828
docker build -f Dockerfile.build -t meshchatx-build:local .
Build only a wheel:
T282828
docker build -f Dockerfile.build --build-arg MESHCHATX_BUILD_TARGETS=wheel -t meshchatx-build:wheel .
Copy T383838/artifacts from the finished image to the host:
T282828
cid=$(docker create meshchatx-build:local)
docker cp "${cid}:/artifacts" ./meshchatx-artifacts
docker rm "${cid}"
Architecture Support Summary
• Docker image: T383838amd64, T383838arm64
• Linux AppImage: T383838x64, T383838arm64
• Linux DEB: T383838x64, T383838arm64
• Windows: T383838x64, T383838arm64 (build scripts available)
• macOS: build scripts available (T383838arm64, T383838universal) for local build environments
• Android: universal APK only (see android/README.md)
Android
MeshChatX supports native Android APK builds (not only Termux).
Build APKs from source
From repo root:
T282828
# 1) Build Chaquopy wheels used by android/app/build.gradle
bash scripts/build-android-wheels-local.sh
# 2) Build universal APKs (one debug + one release per run; see android/README.md)
cd android
./gradlew --no-daemon :app:assembleDebug :app:assembleRelease
Offline Android builds are supported by setting T383838MESHCHATX_OFFLINE_BUILD=1:
T282828
MESHCHATX_OFFLINE_BUILD=1 ./gradlew --no-daemon :app:assembleRelease
This skips the repository wheels fetch step. The T383838android/vendor/ wheels and T383838meshchatx/public/repository-server-bundled/bundled/ must already be present.
There is a single Android variant. Gradle syncs the full T383838meshchatx/ tree into T383838app/src/main/python/meshchatx/, including the offline repository wheel bundle. Published and documented builds use universal packaging only: one debug APK and one release APK per run, each containing the native ABIs configured in T383838android/app/build.gradle.
• Debug: T383838android/app/build/outputs/apk/debug/app-debug.apk
• Release: T383838android/app/build/outputs/apk/release/app-release-unsigned.apk
Notes:
• Release outputs are unsigned by default unless you configure signing (T383838scripts/sign-android-apks.sh).
• Native ABIs embedded in the universal APK follow T383838android/app/build.gradle (including T383838armeabi-v7a when enabled). Building wheels for T383838armeabi-v7a needs an Android SDK on T383838ANDROID_HOME (see T383838android/README.md).
• If repo root T383838dist/reticulum_meshchatx-*.whl exists (for example from T383838python -m build --wheel -o dist .), bundled repository refresh prefers that wheel over PyPI for the MeshChatX package. CI builds that wheel before the Android Gradle step.
Additional docs:
Configuration
MeshChatX supports both CLI args and env vars.
┌────────────────────────┬──────────────────────────────────────┬──────────────┬───────────────────┐
│ Argument │ Environment Variable │ Default │ Description │
├────────────────────────┼──────────────────────────────────────┼──────────────┼───────────────────┤
│ T383838--host │ T383838MESHCHAT_HOST │ T383838127.0.0.1 │ Web server bind … │
│ T383838--port │ T383838MESHCHAT_PORT │ T3838388000 │ Web server port │
│ T383838--no-https │ T383838MESHCHAT_NO_HTTPS │ T383838false │ Disable HTTPS │
│ T383838--ssl-cert / T383838--ssl-key │ T383838MESHCHAT_SSL_CERT / T383838MESHCHAT_SSL_KEY │ (none) │ PEM certificate … │
│ T383838--rns-log-level │ T383838MESHCHAT_RNS_LOG_LEVEL │ (none) │ Reticulum (RNS) … │
│ T383838--headless │ T383838MESHCHAT_HEADLESS │ T383838false │ Do not auto-laun… │
│ T383838--auth │ T383838MESHCHAT_AUTH │ T383838false │ Enable basic auth │
│ T383838--reset-password │ T383838MESHCHAT_RESET_PASSWORD │ T383838false │ Clear the stored… │
│ T383838--storage-dir │ T383838MESHCHAT_STORAGE_DIR │ T383838./storage │ Data directory │
│ T383838--public-dir │ T383838MESHCHAT_PUBLIC_DIR │ auto/bundled │ Frontend files d… │
└────────────────────────┴──────────────────────────────────────┴──────────────┴───────────────────┘
Branches
┌────────┬─────────────────────────────────────────────────────────────────┐
│ Branch │ Purpose │
├────────┼─────────────────────────────────────────────────────────────────┤
│ T383838master │ Stable releases. Production-ready code only. │
│ T383838dev │ Active development. May contain breaking or incomplete changes. │
└────────┴─────────────────────────────────────────────────────────────────┘
Development
Common tasks from T383838Taskfile.yml:
T282828
task install
task format
task lint
task test
task build
T383838Makefile targets are thin shims that delegate to T383838task (same commands as above):
┌──────────────────────┬──────────────┬───────────────────────────────────────────────┐
│ Command │ Delegates to │ Description │
├──────────────────────┼──────────────┼───────────────────────────────────────────────┤
│ T383838make install │ T383838task install │ Install pnpm and UV dependencies │
│ T383838make run │ T383838task run │ Run MeshChatX via UV │
│ T383838make build │ T383838task build │ Build frontend and backend artifacts │
│ T383838make format │ T383838task format │ Format frontend and backend code │
│ T383838make lint │ T383838task lint │ ESLint, vue-tsc, knip, Ruff, and basedpyright │
│ T383838make test │ T383838task test │ Run frontend and backend tests │
│ T383838make clean │ T383838task clean │ Remove build artifacts and node_modules │
│ T383838make tree-rsm-verify │ (shell) │ Verify T383838meshchatx.rsm signature and hashes │
│ T383838make tree-rsm-sign │ (shell) │ Sign tree inventory (requires T383838RNS_ID_PATH) │
│ T383838make hooks-install │ (shell) │ Enable tracked pre-commit RSM resign hook │
└──────────────────────┴──────────────┴───────────────────────────────────────────────┘
Versioning
Current version in this repo is T3838384.8.2.
• T383838package.json T383838version is the only value you edit for a release bump.
• Run T383838pnpm run version:sync (also run at the start of T383838pnpm run build) to propagate that version into T383838pyproject.toml, T383838meshchatx/src/version.py, T383838THIRD_PARTY_NOTICES.txt (product line), README / lang/README.\* “current version” lines, T383838docs/meshchatx_on_raspberry_pi.md pipx example, and T383838packaging/arch/PKGBUILD helpers.
• T383838meshchatx.__version__ is read from T383838meshchatx/src/version.py without importing T383838meshchatx.src, so a plain T383838import meshchatx stays lightweight.
• Changelog entries stay manual when you cut a release.
Troubleshooting: database corruption and full data reset
If MeshChatX fails to start with errors such as T383838database disk image is malformed, T383838DatabaseError, or corrupted ratchet data, the desktop crash screen now offers recovery actions:
• Restore latest backup from T383838database-backups/ or T383838snapshots/ inside your MeshChatX storage folder
• Choose backup file to restore a T383838.zip you saved elsewhere
• Try auto-repair to relaunch with T383838--auto-recover (SQLite checkpoint / integrity pass)
• Emergency mode to open the app without the database so you can export data from About when possible
• Copy reset instructions with the exact folders to delete for a clean reinstall
Where MeshChatX stores data
┌──────────────────┬────────────────────────────────────────────┬──────────────────────────────────┐
│ Platform │ MeshChatX storage │ Reticulum network stack │
├──────────────────┼────────────────────────────────────────────┼──────────────────────────────────┤
│ Linux / macOS │ T383838~/.reticulum-meshchatx/ │ T383838~/.reticulum/ │
│ Windows │ T383838%USERPROFILE%.reticulum-meshchatx │ T383838%USERPROFILE%.reticulum │
│ Windows portable │ T383838<MeshChatX.exe folder>.reticulum-meshchatx │ T383838<MeshChatX.exe folder>.reticulum │
└──────────────────┴────────────────────────────────────────────┴──────────────────────────────────┘
Legacy Reticulum MeshChat data may still exist at T383838~/.reticulum-meshchat/ (or the Windows equivalent).
Automatic database backups are written to:
T383838database-backups/ inside the MeshChatX storage folder when the app has run successfully before.
Complete removal (start fresh)
Quit MeshChatX completely. On Windows, also end T383838ReticulumMeshChatX.exe in Task Manager if it is still running. Then delete the MeshChatX storage folder and the Reticulum config folder for your install type (see table above). This removes your local identity, messages, contacts, path cache, and ratchet state. The next launch creates a new identity unless you restore from a backup first.
Linux / macOS example:
T282828
rm -rf ~/.reticulum-meshchatx ~/.reticulum ~/.reticulum-meshchat
Windows (PowerShell) example:
T282828
Remove-Item -Recurse -Force "$env:USERPROFILE\\.reticulum-meshchatx", "$env:USERPROFILE\\.reticulum", "$env:USERPROFILE\\.reticulum-meshchat" -ErrorAction SilentlyContinue
Custom installs: if you pass T383838--storage-dir or T383838--reticulum-config-dir, remove those directories instead of the defaults.
Command-line restore (advanced)
When the backend can start briefly or you run from source:
T282828
meshchatx --storage-dir /path/to/storage --restore-db /path/to/backup.zip
Security
Security and integrity details:
• LEGAL.md
• Built-in integrity checks and HTTPS/WSS defaults in app runtime.
• CI and release builds on GitHub Actions.
Adding a Language
My workflow: ArgosTranslate -> Local LLM (Qwen 3 + Gemma 4)
People are then welcome to submit fixes to me via LXMF (T383838f489752fbef161c64d65e385a4e9fc74) or wherever you can contact me.
Locale discovery is automatic. Add a new file under T383838meshchatx/src/frontend/locales/ (for example T383838xx.json) with the same keys as T383838en.json and a top-level T383838_languageName string for the label shown in the language selector. You can copy T383838en.json and translate every value by hand; machine-assisted generation is optional and never required.
Optional: Argos Translate bootstrap -- If you want a machine-generated first draft from T383838en.json, you can use T383838scripts/argos_translate.py. It handles formatting, color output, and helps protect interpolation variables (like T383838{count}) from accidental edits.
T282828
# Install argostranslate if you haven't already
pipx install argostranslate
# Run the translation script
python scripts/argos_translate.py --from en --to xx --input meshchatx/src/frontend/locales/en.json --output meshchatx/src/frontend/locales/xx.json --name "Your Language Name"
After any machine-assisted pass, have an LLM or a human reviewer verify grammar, context, and tone (for example formal vs informal).
Run T383838pnpm test -- tests/frontend/i18n.test.js --run to verify key parity with T383838en.json.
No other code changes are required. The app, language selector, and tests all discover locales from the T383838meshchatx/src/frontend/locales/ directory at build time.
Donation
Donations are voluntary. They help fund time and effort to develop this app.
Ways to give: donate.md (Monero, Ko-Fi, Buy Me a Coffee).
Credits
• Liam Cottle - Original Reticulum MeshChat
• RFnexus - micron parser JavaScript work
• markqvist - Reticulum, LXMF, LXST
License
Project-owned portions are licensed under 0BSD.
Original upstream portions from Reticulum MeshChat remain under MIT.
See LICENSE for full text and notices.
Served by rngit 1.4.1 - Generated in 0.05s